#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <string>
#include <functional>
#include "log.hpp"
#define BACKLOG 10
Log log;

using namespace std;
class Sock
{
public:
    Sock();
    ~Sock();
    void Socket();
    void Bind(uint16_t port);
    void Listen();
    int Accept(string* ip, uint16_t* port);
    bool Connect(const string& ip, const uint16_t& port);
    int GetFd();
    void Close();
private:
    int _socketFd;
};

Sock::Sock() : _socketFd(-1)
{}

Sock::~Sock()
{}

inline void Sock::Socket()
{
    _socketFd = socket(AF_INET, SOCK_STREAM, 0);
    if(_socketFd < 0) {
        log(FATAL, "Sock::Socket() error! why: %s\n", strerror(errno));
        exit(-1);
    }
}

inline void Sock::Bind(uint16_t port)
{
    sockaddr_in local;
    memset(&local, 0, sizeof local);
    local.sin_family = AF_INET;
    local.sin_port = htons(port);
    local.sin_addr.s_addr = INADDR_ANY;

    if(bind(_socketFd, (sockaddr*)&local, sizeof local) < 0) {
        log(FATAL, "Sock::Bind() error! why: %s\n", strerror(errno));
        exit(-1);
    }
}

inline void Sock::Listen()
{
    if(listen(_socketFd, BACKLOG) < 0) {
        log(FATAL, "Sock::Listen() error!\n");
        exit(-1);
    }
}

inline int Sock::Accept(string* peerIp, uint16_t* peerPort)
{
    sockaddr_in peer;
    memset(&peer, 0, sizeof peer);
    socklen_t len = sizeof len;

    int socketFd = accept(_socketFd, (sockaddr*)&peer, &len);
    if(socketFd < 0) {
        log(WARNING, "Sock::Accept() error!\n");
        return -1;
    }

    // 将客户端的ip输出出去
    char buf[64];
    if(inet_ntop(AF_INET, &peer.sin_addr, buf, sizeof buf) == nullptr) {
        log(WARNING, "Sock::Accept() error!\n");
        return -1;
    }
    *peerIp = buf;

    // 将客户端的端口号输出出去
    *peerPort = ntohs(peer.sin_port);
    return socketFd;
}

inline bool Sock::Connect(const string& ip, const uint16_t& port)
{
    sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    inet_pton(AF_INET, ip.c_str(), &server.sin_addr.s_addr);

    int ret = connect(_socketFd, (sockaddr*)&server, sizeof server);
    if(ret < 0) {
        cerr << "Sock::Connect error!" << endl;
        return false;
    }
    return true;
}

inline int Sock::GetFd()
{
    return _socketFd;
}

inline void Sock::Close()
{
    close(_socketFd);
}
